package by.bsu.diploma.ga;

import by.bsu.diploma.common.Grid;
import by.bsu.diploma.common.Orientation;
import by.bsu.diploma.common.Util;
import by.bsu.diploma.common.Word;
import by.bsu.diploma.system.Configuration;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;

import static by.bsu.diploma.common.Constants.FILLED;
import static by.bsu.diploma.common.Constants.INTERSECTION;
import static by.bsu.diploma.common.Orientation.HORIZONTAL;
import static by.bsu.diploma.common.Orientation.VERTICAL;

/**
 * @author Amatoly Selitsky
 */
public class GenomeGenerator {

    private Grid grid;
    private List<Word> words;
    private int total;

    public GenomeGenerator(Configuration configuration) {
        this.grid = new Grid(configuration.getRowCount(), configuration.getColCount());
        this.words = configuration.getWords();
        this.total = Util.calcTotal(words);
    }

    public Genome generate() {
        if (words == null) {
            throw new IllegalArgumentException("List words can not be empty");
        }
        Collections.sort(words);
        Random random = new Random();
        int i = 0, genWordCount = random.nextInt(total + 1);
        List<Word> placedWords = new ArrayList<Word>();
        for(Word word : words) {
            for(int k = 0; k < word.getCount(); k++) {
                if (i == genWordCount) {
                    break;
                }
                placedWords.add(populate(word));
                i++;
            }
        }
        return new Genome(grid, placedWords);
    }

    private Word populate(Word word) {
        Random random = new Random();
        int row, col, length = word.getLength();
        row = random.nextInt(grid.getRowCount());
        col = random.nextInt(grid.getColumnCount());
        if (row + length - 1 < grid.getRowCount()
                && isCanPopulate(row, col, length, VERTICAL)) {
            addWordOnGrid(row, col, length, VERTICAL);
            word.update(row, col, VERTICAL);
        } else if (col + length - 1 < grid.getColumnCount()
                && isCanPopulate(row, col, length, HORIZONTAL)) {
            addWordOnGrid(row, col, length, HORIZONTAL);
            word.update(row, col, HORIZONTAL);
        } else if (row - length + 1 >= 0
                && isCanPopulate(row - length + 1, col, length, VERTICAL)) {
            addWordOnGrid(row - length + 1, col, length, VERTICAL);
            word.update(row - length + 1, col, VERTICAL);
        } else if (col - length + 1 >= 0
                && isCanPopulate(row, col - length + 1, length, HORIZONTAL)) {
            addWordOnGrid(row, col - length + 1, length, HORIZONTAL);
            word.update(row, col - length + 1, HORIZONTAL);
        } else {
            populate(word);
        }
        return word;
    }

    private boolean isCanPopulate(int row, int col, int length, Orientation orientation) {
        int count = 0, cells[][] = grid.getCells();
        boolean isFilledFirst, isFilledLast, isNotFilledPrevious = false, isNotFilledNext = false;
        if (VERTICAL.equals(orientation)) {
            if (row > 0) {
                isNotFilledPrevious = (cells[row - 1][col] != FILLED && cells[row - 1][col] == INTERSECTION);
            }
            isFilledFirst = (cells[row][col] == FILLED || cells[row][col] == INTERSECTION);
            isFilledLast = (cells[row + length - 1][col] == FILLED || cells[row + length - 1][col] == INTERSECTION);
            for (int i = row; i < row + length; i++) {
                if (cells[i][col] == FILLED || cells[i][col] == INTERSECTION) {
                    count++;
                }
            }
            if (row + length < grid.getRowCount() - 1) {
                isNotFilledNext = (cells[row + length][col] != FILLED && cells[row + length][col] != INTERSECTION);
            }
        } else {
            if (col > 0) {
                isNotFilledPrevious = (cells[row][col - 1] != FILLED && cells[row][col - 1] != INTERSECTION);
            }
            isFilledFirst = (cells[row][col] == FILLED || cells[row][col] == INTERSECTION);
            isFilledLast = (cells[row][col + length - 1] == FILLED || cells[row][col + length - 1] == INTERSECTION);
            for (int i = col; i < col + length; i++) {
                if (cells[row][i] == INTERSECTION || cells[row][i] == FILLED) {
                    count++;
                }
            }
            if (col + length < grid.getColumnCount() - 1) {
                isNotFilledNext = (cells[row][col + length] != FILLED && cells[row][col + length] != INTERSECTION);
            }
        }
        return (!isFilledFirst && !isFilledLast && (count != length) && isNotFilledPrevious && isNotFilledNext);
    }

    public void addWordOnGrid(int row, int col, int length, Orientation orientation) {
        int cells[][] = grid.getCells();
        if (VERTICAL.equals(orientation)) {
            for (int i = row; i < row + length; i++) {
                if (cells[i][col] == FILLED || cells[i][col] == INTERSECTION) {
                    cells[i][col] = INTERSECTION;
                } else {
                    cells[i][col] = FILLED;
                }
            }
        } else {
            for (int i = col; i < col + length; i++) {
                if (cells[row][i] == INTERSECTION || cells[row][i] == FILLED) {
                    cells[row][i] = INTERSECTION;
                } else {
                    cells[row][i] = FILLED;
                }
            }
        }
    }

}
